package com.gitee.beiding.template_execel;

import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//提取器
public class Extractor {


    static {
        ZipSecureFile.setMinInflateRatio(-1d);
    }

    public static ExtractResult extract(XSSFSheet template, XSSFSheet data, Map<String, Class<?>> entityMapping) {
        return extract(template, data, entityMapping, ColNumberMatchingMode.EQUALS);
    }

    /**
     * 指定提取模式
     *
     * @param template      模板sheet页
     * @param data          数据sheet页
     * @param entityMapping 映射实体
     * @return 提取的结果
     * @param colModel      提取的列合并模式
     */
    public static ExtractResult extract(XSSFSheet template, XSSFSheet data, Map<String, Class<?>> entityMapping, ColNumberMatchingMode colModel) {

        DoubleActingIndex doubleActingIndex = new DoubleActingIndex();

        Map<String, List<ValueHolder>> colValueMap = new HashMap<>();

        TemplateSheetHolder templateSheetHolder = new TemplateSheetHolder(template, colValueMap, colModel);
        DataSheetHolder dataSheetHolder = new DataSheetHolder(data, templateSheetHolder);
        doubleActingIndex.setLeft(templateSheetHolder);
        doubleActingIndex.setRight(dataSheetHolder);

        while (doubleActingIndex.next()) {
        }

        ValueHandler valueHandler = new ValueHandler();
        valueHandler.setEntityMapping(entityMapping);

        Map<String, Object> handle = valueHandler.handle(colValueMap);


        //js回收
        Js.recycle();

        return new ExtractResult(handle);
    }

    /**
     * 读取多个sheet页中的数据
     *
     * @param templateMap   模板map
     * @param data          数据文件
     * @param entityMapping 映射实体
     * @return 提取的结果
     * @throws IOException  读写时可能发生io异常
     */
    public static ExtractResult extract(Map<String, XSSFSheet> templateMap, InputStream data, Map<String, Class<?>> entityMapping) throws IOException {
        return extract(templateMap, PoiUtils.read(data), entityMapping);
    }

    public static ExtractResult extract(Map<String, XSSFSheet> templateMap, XSSFWorkbook data, Map<String, Class<?>> entityMapping) throws IOException {
        Map<String, Object> map = new HashMap<>();
        for (int i = 0; i < data.getNumberOfSheets(); i++) {
            XSSFSheet d = data.getSheetAt(i);
            XSSFSheet t = templateMap.get(d.getSheetName());
            if (t == null) {
                continue;
            }
            map.putAll(extract(t, d, entityMapping).getData());
        }
        return new ExtractResult(map);
    }

    /**
     * 从输入流中读取和template同名的sheet页
     *
     * @param template      模板
     * @param data          数据
     * @param entityMapping 实体映射
     * @return 提取的结果
     * @throws IOException  读写时可能发生io异常
     */
    public static ExtractResult extract(XSSFSheet template, InputStream data, Map<String, Class<?>> entityMapping) throws IOException {
        XSSFWorkbook read = PoiUtils.read(data);
        return extract(template, read, entityMapping);
    }

    /**
     * 使用一个模板sheet提取一个sheet页中的数据相同的sheet页
     *
     * @param template      模板
     * @param data          数据
     * @param entityMapping 实体映射
     * @return 提取的结果
     * @throws IOException  读写时可能发生io异常
     */
    public static ExtractResult extract(XSSFSheet template, XSSFWorkbook data, Map<String, Class<?>> entityMapping) throws IOException {
        Map<String, Object> map = new HashMap<>();
        XSSFSheet d = data.getSheet(template.getSheetName());
        if (d != null) {
            map.putAll(extract(template, d, entityMapping).getData());
        }
        return new ExtractResult(map);
    }

    /**
     * 从输入流中读取模板和数据Excel和template相同的sheet页
     *
     * @param template      模板
     * @param data          数据
     * @param entityMapping 实体映射
     * @return 提取的结果
     * @throws IOException  读写时可能发生io异常
     */
    public static ExtractResult extract(InputStream template, InputStream data, Map<String, Class<?>> entityMapping) throws IOException {
        XSSFWorkbook tb = PoiUtils.read(template);
        XSSFWorkbook db = PoiUtils.read(data);
        return extract(tb, db, entityMapping);
    }


    /**
     * 从数据Excel中读取和模板Excel中同名的sheet页中的数据
     *
     * @param template      模板
     * @param data          数据
     * @param entityMapping 实体映射
     * @return 提取的结果
     * @throws IOException  读写时可能发生io异常
     */
    public static ExtractResult extract(XSSFWorkbook template, XSSFWorkbook data, Map<String, Class<?>> entityMapping) throws IOException {

        Map<String, XSSFSheet> sheetMap = new HashMap<>();
        for (int i = 0; i < template.getNumberOfSheets(); i++) {
            XSSFSheet d = template.getSheetAt(i);
            sheetMap.put(d.getSheetName(), d);
        }
        return extract(sheetMap, data, entityMapping);
    }



    private static Map<Integer, Map<Integer, Merge>> handleMerge(List<CellRangeAddress> cellRangeAddresses) {
        Map<Integer, Map<Integer, Merge>> map = new HashMap<>();
        if (cellRangeAddresses.size() > 0) {
            for (CellRangeAddress cellRangeAddress : cellRangeAddresses) {
                Map<Integer, Merge> absent = map.computeIfAbsent(cellRangeAddress.getFirstRow(), k -> new HashMap<>());
                absent.computeIfAbsent(cellRangeAddress.getFirstColumn(), k -> Merge.get(cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow() + 1, cellRangeAddress.getLastColumn() - cellRangeAddress.getFirstColumn() + 1));
            }
        }
        return map;
    }

    private static class TemplateSheetHolder implements DoubleActingIndex.Handle {

        XSSFSheet template;

        //当前指针
        int current;

        //指针最大
        int max;

        Map<Integer, Map<Integer, Merge>> margeMap;

        Map<String, List<ValueHolder>> colValueMap;

        private ColNumberMatchingMode colModel;

        TemplateSheetHolder(XSSFSheet template, Map<String, List<ValueHolder>> colValueMap, ColNumberMatchingMode colModel) {
            this.colModel = colModel;
            this.colValueMap = colValueMap;
            this.template = template;
            this.current = template.getFirstRowNum();
            this.max = template.getLastRowNum();
            this.margeMap = handleMerge(template.getMergedRegions());

            //初始化的时候跳转一次
            next();
        }

        TemplateRow templateRow;

        Map<Integer, Merge> empty = Collections.emptyMap();

        Map<Integer, Merge> getMerge(int row) {
            Map<Integer, Merge> mergeMap = margeMap.get(row);
            if (mergeMap == null) {
                mergeMap = empty;
            }
            return mergeMap;
        }

        @Override
        public boolean next() {
            while (true) {

                if (current > max) {
                    return false;
                }
                templateRow = TemplateRow.compile(template.getRow(current), getMerge(current), colValueMap, colModel);

                current++;
                if (templateRow != null) {//空白行不能作为模板行,没有任何意义
                    return true;
                }

            }
        }

        @Override
        public boolean shouldChange() {//总是跳转到另一行中
            return true;
        }

        @Override
        public void afterChange() {

        }

        @Override
        public void afterChangeTo() {

        }

        TemplateRow getTemplateRow() {
            return templateRow;
        }
    }


    private static class DataSheetHolder implements DoubleActingIndex.Handle {

        XSSFSheet data;

        //当前指针
        int current;

        //指针最大
        int max;

        Map<Integer, Map<Integer, Merge>> margeMap;

        XSSFRow row;

        Map<Integer, Merge> empty = Collections.emptyMap();

        Map<Integer, Merge> getMerge(int row) {
            Map<Integer, Merge> mergeMap = margeMap.get(row);
            if (mergeMap == null) {
                mergeMap = empty;
            }
            return mergeMap;
        }

        //可用于获取模板行数据
        TemplateSheetHolder templateSheetHolder;

        DataSheetHolder(XSSFSheet data, TemplateSheetHolder templateSheetHolder) {
            this.templateSheetHolder = templateSheetHolder;
            this.data = data;
            this.current = data.getFirstRowNum();
            this.max = data.getLastRowNum();
            margeMap = handleMerge(data.getMergedRegions());
        }

        @Override
        public boolean next() {
            while (true) {
                if (current > max) {
                    return false;
                }
                row = data.getRow(current);
                merge = getMerge(current);
                current++;
                if (row != null) {
                    return true;
                }
            }
        }

        Map<Integer, Merge> merge;

        @Override
        public boolean shouldChange() {

            //TODO 处理跳转逻辑
            TemplateRow templateRow = templateSheetHolder.getTemplateRow();


            boolean b = templateRow.extract(row, merge);

            //表明当前行已经不符合模板行
            if (!b) {

                //指针后移一下,在变更模板行后重新判断当前行中的数据是否符合模板行
                current--;

                //改变模板行
                return true;

            }

            return false;
        }

        @Override
        public void afterChange() {
        }

        @Override
        public void afterChangeTo() {

        }
    }

}
